// locutor
// (c) 2009-2010, Normen Müller, normen.mueller@gmail.com  
// $Id: I.scala 94 2010-06-12 15:35:36Z normen.mueller $
package locutor.g

import java.io.File
import java.net.URI

import scaup.Views._

import scalaxx.diff.Command
import scalaxx.path.LocationPath

import locutor.g.parsing.ImpactGraphParser

/** Companion to <code>ImpactGraph</code> class.
 * 
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.5
 */
object ImpactGraph {
  def empty = new ImpactGraph(Nil, Nil)
  
  def apply(dependencyGraph: DependencyGraph): ImpactGraph = new ImpactGraph(dependencyGraph.nodes, dependencyGraph.edges)
  
  def parse(description: File): ImpactGraph = ImpactGraphParser.parse(description)
}

/** An impact graph.
 * 
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.5
 * 
 *  @todo Concerning a reasonable change impact analysis dependency graph must be revised.
 *    First, XML attributes must be imported as a dependency graph nodes,
 *    the other XML root elements must be taken into account, and
 *    ultimately, the handling of semantic nodes and edges has to be reassessed.
 */
class ImpactGraph private [locutor] (override val nodes: List[Infom], override val edges: List[Link]) extends DependencyGraph(nodes, edges){
  def mark(delta: (File, List[Command])): ImpactGraph = mark(delta._1.toURI, (Map[LocationPath, Command]() /: delta._2)((m,c) => m + {(fix(c.path), c)}))
  
  /* If a location path refers to an attribute axis a subsequent mark operation fails due to the fact XML attributes are no graph nodes.
   * @fixme
   */
  import scalaxx.path._
  private def fix(path: LocationPath): LocationPath = path match {
    case Path(s@(hd :: tl)) => s.last match {case Axis("attribute", _, _) => path.parent case _ => path}
    case Root(s@(hd :: tl)) => s.last match {case Axis("attribute", _, _) => path.parent case _ => path}
    case _                  => path
  }
  
  /**
   *  @note we are using URIs here due to equals/hasCode dilemma in java.net.URL (cf. scaup.data.URIExtras).  
   */  
  private def mark(scope: URI, cmds: Map[LocationPath, Command]) = ((ns: List[Infom]) => {
    /* @fixme If a command affects an XML root element the assertions fails due to the fact the root element is not part of the graph.
     *   Furthermore, if a command affects an XML attribute the assertions fails due to the fact attributes are no graph nodes (cf. #fix(LocationPath)).
     */    
    //assert(cmds.keys forall (key => nodes exists {case Infom(_, _, Some(url), Some(path), _) if (url.toURI == scope) && (key == path) => true case _ => false}))
    
    /* Unfortunately, at the moment, semantic nodes neither have an url nor a path attribute, so we can't map Infoms (e.g. Map[(URI, LocationPath), Infom])
     * for fast retrievel.  This makes the identification highly inefficient.
     * @todo Think about another identifiaction criteria for INFOMs cause semantic nodes won't have an url nor a path.  Maybe we should/ could use the label slot.
     */
    val ident = (n: Infom) => (ns find {case Infom(label, _, url, path, _) if (label == n.label) && (url == n.url) && (path == n.path) => true case _ => false}) match {
      case Some(n) => n
      case None    => error("Fatal error in creating the impact graph.")
    }
    new ImpactGraph(ns, edges map {case Link(src, typ, tgt, as) => Link(ident(src), typ, ident(tgt), as)})      
    })(nodes map (node => node match {
      case Infom(label, typ, u@Some(url), p@Some(path), as) if (url.toURI == scope) && (cmds contains path) =>
        Infom(label, typ, u, p, as map (_ match {
          case StringAttribute(key, value) if key == "effect" => SimpleAttribute(key, Some("local"))
          case SetAttribute(key, typ, value) if key == "ct"   => SetAttribute(key, typ, value + cmds(path).prefix)
          case a@_                                            => a 
        }))
      case _ => node
    }))    
}
